// SPDX-License-Identifier: GPL-2.0+

#include <common.h>
#include <pci.h>
#include <usb.h>
#include <scsi.h>
#include <ahci.h>
#include <spi.h>
#include <spi_flash.h>
#include <led.h>
#include <wdt.h>
#include <asm/io.h>

#include <dm.h>
#ifdef CONFIG_DM_VIDEO
#include <video_console.h>
#include <video_font.h>
#endif
#include <mach/ls3x.h>
#include <mach/ls7a.h>

#include "platform.h"

extern void user_env_fetch(void);

int __weak ls3x_board_late_init(void)
{
	return 0;
}

void __weak superio_init(void)
{
}

int checkboard(void)
{
	printf("Board: %s\n", CONFIG_LOONGSON_BOARD_NAME);
	return 0;
}

int __weak vidconsole_notice(char *notice)
{
#ifdef CONFIG_DM_VIDEO
	struct vidconsole_priv *priv;
	struct udevice *con;
	int col, row, len;

	for (uclass_first_device(UCLASS_VIDEO_CONSOLE, &con);
				con; uclass_next_device(&con)) {
		priv = dev_get_uclass_priv(con);
#if 1
		//显示bootloader版本信息
		{
		char buf[DISPLAY_OPTIONS_BANNER_LENGTH] = {0};
		display_options_get_banner(false, buf, DISPLAY_OPTIONS_BANNER_LENGTH);
		len = strcspn(buf, "\n");
		buf[len] = 0;
		col = (priv->cols / 2) - (len / 2);
		if (col < 0)
			col = 0;
		row = priv->rows - 1;
		vidconsole_position_cursor(con, col, row);
		vidconsole_put_string(con, buf);
		}
#endif

		//显示操作按键提示信息
		len = strlen(notice);
		col = (priv->cols / 2) - (len / 2);
		if (col < 0)
			col = 0;
		row = priv->rows - 2;
		vidconsole_position_cursor(con, col, row);
		vidconsole_put_string(con, notice);
		vidconsole_position_cursor(con, 0, 0);
	}
#endif
	return 0;
}

static void print_notice(void)
{
	char notice[DISPLAY_OPTIONS_BANNER_LENGTH];

	sprintf(notice, "Press %s, %s", NOTICE_STR1, NOTICE_STR2);

	//显示屏控制台显示提示信息
	vidconsole_notice(notice);
	//串口控制台显示提示信息
	printf("********Notice********\n");
	printf("%s\r\n", notice);
	printf("**********************\n");
}

int board_late_init(void)
{
	if (IS_ENABLED(CONFIG_LED)) {
		led_default_state();
	}

	print_notice();
	ls3x_board_late_init();

	user_env_fetch();

	superio_init();

	return 0;
}

#ifdef CONFIG_SCSI_AHCI_PLAT
void scsi_init(void)
{
	pci_dev_t busdevfunc;
	void __iomem *ahci_base;

	busdevfunc = pci_find_device(SCSI_VEND_ID, SCSI_DEV_ID, 0); /* get PCI Device ID */
	if (busdevfunc == -1) {
		printf("SCSI Controller (%04X,%04X) not found\n", SCSI_VEND_ID, SCSI_DEV_ID);
	}
	ahci_base = pci_map_bar(busdevfunc, 0x10, PCI_REGION_MEM);
	printf("scsi ahci plat %p\n", ahci_base);
	if(!ahci_init(ahci_base))
		scsi_scan(1);
}
#endif


extern int tlb_init_default(void);

#ifdef CONFIG_SB_LS7A1000
//LS7A1000 SPI 外接flash，用于存放两个GMAC的MAC地址
#define CFG_MAC_ADDR_SPI_BUS	1
#define CFG_MAC_ADDR_SPI_CS	0
#define CFG_MAC_ADDR_SPI_MAX_HZ	CONFIG_SF_DEFAULT_SPEED
#define CFG_MAC_ADDR_SPI_MODE	SPI_MODE_3

static int get_mac_addr(u8 *addr, int offset)
{
	struct spi_flash *flash;
	int ret;

	flash = spi_flash_probe(CFG_MAC_ADDR_SPI_BUS, CFG_MAC_ADDR_SPI_CS,
			CFG_MAC_ADDR_SPI_MAX_HZ, CFG_MAC_ADDR_SPI_MODE);
	if (!flash) {
		printf("Error - unable to probe SPI flash.\n");
		ret = -1;
		goto over;
	}

	ret = spi_flash_read(flash, offset, ARP_HLEN, addr);
	if (ret) {
		printf("Error - unable to read MAC address from SPI flash.\n");
		ret = -1;
		goto over;
	}

	if (!is_valid_ethaddr(addr)) {
		printf("Warning: Invalid "
			"MAC address read from SPI flash\n");
		ret = -1;
	}
over:
//	spi_flash_free(flash);

	return ret;
}

static void set_mac_addr(void)
{
	uchar env_enetaddr[ARP_HLEN];
	int enetaddr_found;
	int spi_mac_read;
	uchar buff[ARP_HLEN];
	int index;

	for (index=0; index<2; index++) {
		enetaddr_found = eth_env_get_enetaddr_by_index("eth", index, env_enetaddr);
		spi_mac_read = get_mac_addr(buff, index * 0x10);

		/*
		 * MAC address not present in the environment
		 * try and read the MAC address from SPI flash
		 * and set it.
		 */
		if (!enetaddr_found) {
			if (!spi_mac_read) {
				if (eth_env_set_enetaddr_by_index("eth", index, buff)) {
					printf("Warning: Failed to "
					"set MAC address from SPI flash\n");
				}
			}
		} else {
			/*
			 * MAC address present in environment compare it with
			 * the MAC address in SPI flash and warn on mismatch
			 */
			if (!spi_mac_read && memcmp(env_enetaddr, buff, 6)) {
				printf("Warning: MAC address in SPI flash don't match "
						"with the MAC address in the environment\n");

				char mac[ARP_HLEN_ASCII + 1];
				char enetvar[32];
				sprintf(enetvar, index ? "%s%daddr" : "%saddr", "eth", index);
				sprintf(mac, "%pM", mac);
				if (env_set(enetvar, mac)) {
					printf("Warning: Failed to "
					"set MAC address from SPI flash\n");
				}
			}
		}
	}
}
#endif

int misc_init_r(void)
{
//	u32 val;

	/* disable spi instruct fetch before enter spi io mode
	重置cache通路二级窗口0的基地址？ */
//	val = readl((unsigned int *)CKSEG1ADDR(LS2X_CPU_WIN0_MMAP));
//	if (val == 0x1fc000f2) {
//		writel(0x1fc00082, (unsigned int *)CKSEG1ADDR(LS2X_CPU_WIN0_MMAP));
//	}
#ifdef CONFIG_SB_LS7A1000
	set_mac_addr();
#endif

	return 0;
}

#ifdef CONFIG_BOARD_EARLY_INIT_R
extern char waitforinit(void);

__weak void ls3a_board_init(void)
{
}

extern int FontSideBusInit(int NodeNum);
extern void ChipsetInit(int NodeNum, int memsz);

int board_early_init_r(void)
{
	//core1 run wait_for_smp_call function in ram
	asm volatile (
	".set push;.set noreorder;.set mips64;\n"
	"dli $2,0x900000003ff01100;\n"
	"dli $3, 3;\n"
	"1:sd %0,0x20($2);\n"
	"daddiu $2,0x100;\n"
	"addiu $3,-1;\n"
	"bnez $3, 1b;\n"
	"nop;\n"
	".set reorder;\n"
	".set mips0;\n"
	".set pop;\n"
	::"r"(&waitforinit):"$2","$3");

	tlb_init_default();
#ifndef CONFIG_SB_LS7A1000_INIT_ASM
	int memorysize_total;

#ifdef CONFIG_CPU_LOONGSON3A4000
	memorysize_total = gd->arch.memorysize_total >> 30;
#else
	memorysize_total = gd->arch.memorysize_total >> 29;
#endif
	FontSideBusInit(1);
	ChipsetInit(1, memorysize_total);
#endif

#ifdef CONFIG_DM_PCI
	/*
	 * Make sure PCI bus is enumerated so that peripherals on the PCI bus
	 * can be discovered by their drivers
	 */
	pci_init();
#endif

	ls3a_board_init();

	return 0;
}
#endif

#ifdef CONFIG_RECOVER
extern int recover(void);
#endif

#ifdef CONFIG_LAST_STAGE_INIT
int last_stage_init(void)
{
	/* start usb so that usb keyboard can be used as input device */
	if (CONFIG_IS_ENABLED(USB_KEYBOARD)) {
		usb_init();
	}

	/* 某些linux内核(3.10)或许系统并不会自动发现sata设备，需要bootloader主动扫描sata设备 */
	if (CONFIG_IS_ENABLED(SCSI)) {
#ifndef CONFIG_DM_SCSI
		scsi_bus_reset(NULL);
#endif
		scsi_scan(true);
	}

	/* 上电时长按按钮3秒进入recover功能, recover优先顺序usb>mmc>sata
       按键使用CONFIG_RECOVER_GPIO_BUTTON定义的gpio */
#ifdef CONFIG_RECOVER
	recover();
#endif

	return 0;
}
#endif

#if defined(CONFIG_SYS_CONSOLE_IS_IN_ENV) && \
defined(CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE)
int overwrite_console(void)
{
	env_set("stdout", "serial");

	return 0;
}
#endif

#ifdef CONFIG_CPU_LOONGSON3A4000
//LS3A4000支持扩展中断模式，增加中断分发、中断处理的灵活性
//这里把扩展中断模式的HT控制器的外部中断转换寄存器配置为正确的值
#define LOONGSON_HT1_INT_TRANS_ADDR 0x90000efdfb000270ULL
u64 __raw__readq(u64 addr);
u64 __raw__writeq(u64 addr, u64 val);
void loongson_ht_trans_init(void)
{
	int node_num = 1;
	int i;
	u64 node_addr;

	for (i=0; i<node_num; i++) {
		node_addr = (((u64)i << 44) | LOONGSON_HT1_INT_TRANS_ADDR);
		#ifdef CONFIG_64BIT
		__raw_writeq(0x400000001fe01140ULL, (void *)node_addr);
		#else
		__raw__writeq(node_addr, 0x400000001fe01140ULL);
		#endif
	}
}
#endif

void arch_preboot_os(void)
{
#ifdef CONFIG_WATCHDOG
	wdt_stop(gd->watchdog_dev);
#endif
}
